home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Pascal Super Library
/
Pascal Super Library (CW International)(1997).bin
/
LIBRARY
/
BPL70N16
/
ARISOURC.ZIP
/
FPSIN.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-03-07
|
8KB
|
164 lines
; *******************************************************
; * *
; * Turbo Pascal Runtime Library Version 7.0 *
; * Real Sine/Cosine *
; * *
; * Copyright (C) 1989-1993 Norbert Juffa *
; * *
; *******************************************************
TITLE FPSIN
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE
; Externals
EXTRN RealAdd:NEAR,RealSub:NEAR,RealPoly:NEAR,RealMulNoChk:NEAR,
EXTRN HaltError:NEAR,RealTrunc:NEAR,RealMul:NEAR,RealFloat:NEAR
EXTRN RealReduceMP:NEAR,RFrac:FAR,RealMulFNoChk:NEAR
; Publics
PUBLIC RSin,RCos
;-------------------------------------------------------------------------------
; RSin and RCos are different entries into the same routine that computes the
; sine as well as the cosine, as cosine is the same as the sine with an offset
; of pi/2. The argument is reduced to the range -pi/4..+pi/4. Depending on the
; octant into which the argument falls and the function to be computed, the
; cosine or sine of the reduced argument is computed by polynomial approxi-
; mations to the sine/cosine. To prevent excessive loss of precision in the
; argument reduction, the mapping of the argument to the reduced argument is
; done via a pseudo extended precision calculation. However this extended
; precision calculation is only available for arguments inside the range
; -1.68e+9..1.68e+9. Outside this range, a simple argument reduction is used.
; This causes a sudden drop in accuracy if the absolute value of the argument
; gets larger than 1.68e+9. For arguments bigger than 6.9e12, there is a total
; loss of precision. Outside this range, Sin always returns 0, Cos returns 1.
; The approximating polynomial for the sine on -pi/4..pi/4 is:
;
; P(x) := ((((2.476053850842e-8 *x² + 2.755533965768e-6)*x² -
; 1.984126372865e-4)*x² + 8.333333325083e-3)*x² -
; 1.666666666663e-1)*x²*x + x
;
; This approximation has a theoretical maximum relative error of 1.13e-14.
; Maximum observed error when evaluated in REAL arithmetic is 1.04e-13.
;
; The approximating polynomial for the cosine on -pi/4..pi/4 is:
;
; P(x) := ((((-2.715314622037e-7 *x² + 2.479862035332e-5)*x² -
; 1.388887879411e-3)*x² + 4.166666651281e-2)*x² -
; 4.999999999923e-1)*x² + 1.000000000000e+0
;
; This approximation has a theoretical maximum relative error of 1.41e-13.
; Maximum observed error when evaluated in REAL arithmetic is 1.49e-12.
;
; INPUT: DX:BX:AX argument
;
; OUTPUT: DX:BX:AX sin of argument
;
; DESTROYS: AX,BX,CX,DX,SI,DI,Flags
;-------------------------------------------------------------------------------
RCos PROC FAR
PUSH BP ; save caller's frame pointer
AND DH, 7Fh ; abs(x), since cos (x) = cos (abs(x))
MOV BP, 2 ; load function code for cosine
JMP $cos_entry ; goto sine/cosine computation
RCos ENDP
ALIGN 4
RSin PROC FAR
PUSH BP ; save caller's frame pointer
XOR BP, BP ; load function code for sine
$cos_entry: MOV CH, 80h ; load mask to extract sign bit
AND CH, DH ; extract sign bit
XOR DH, CH ; x := abs(x)
PUSH CX ; save sign
$value_ok: MOV CX, 04E81h ; load
MOV SI, 0836Eh ; real constant
MOV DI, 022F9h ; 4/pi
CMP AL, 9Fh ; x >= 2^30 ?
JAE $big_val ; x/(pi/4) too big to be stored in long
PUSH AX ; save x
PUSH BX ; on
PUSH DX ; stack
CALL RealMulFNoChk ; compute x/(pi/4)
XOR CH, CH ; signal truncation desired
CALL RealTrunc ; n = Trunc (x/(pi/4))
ROR AL, 1 ; if quadrant odd, set CF=1
ROL AL, 1 ; restore register, CF=1 if odd quadrant
ADC AX, 0 ; increment quadrant
ADC DX, 0 ; if quadrant odd
ADD BP, AX ; q := quadrant + flag
CALL RealFloat ; convert quadrant to REAL value
POP DI ; get
POP SI ; back
POP CX ; x
TEST BP, 3 ; determine octants that use cos,sin
PUSHF ; save sin/cos flag
PUSH BP ; save q
MOV BP, OFFSET c1 ; pointer to reduction constant table
CALL RealReduceMP ; compute x + n*(pi/2) to full precision
MOV CX, 5 ; use five coefficients
XOR SI, SI ; signal P(x²)*x+x wanted
POP BP ; get back q
POPF ; get back sin/cos flag
JPE $sine ; octants 0, 3, 4, 7 take sine
$cosine: MOV DI,OFFSET CS_COEFF; pointer to 1st coeff. of cos approx.
INC SI ; signal P(x²) wanted
CALL RealPoly ; P(x²) in DX:BX:AX
MOV CX, 00081h ; load
XOR SI, SI ; floating-point
MOV DI, SI ; constant 1.0
CALL RealAdd ; 1 + P(x²)
JMP $make_sign ; put appropriate sign on result
$sine: MOV DI,OFFSET SN_COEFF; pointer to 1st coeff. of sin approx.
CALL RealPoly ; P(x²)*x+x in DX:BX:AX
$make_sign: POP CX ; get sign mask
INC BP ; determine if
TEST BP, 4 ; result has to negated
JZ $ende ; result ok
XOR CH, 80h ; negate sign flag
$ende: POP BP ; restore caller's frame pointer
OR AL, AL ; result zero ?
JZ $end_sin ; yes, done
XOR DH, CH ; make sign bit
$end_sin: RET ; done
$big_val: SUB CL, 3 ; 1/(2*pi) in DI:SI:CX
CALL RealMulNoChk ; x/(2*pi)
CALL RFrac ; frac (x/(2*pi))
MOV CX, 02183h ; load
MOV SI, 0DAA2h ; constant
MOV DI, 0490Fh ; 2*pi
CALL RealMulNoChk ; 2*pi * frac (x/(2*pi)) = remainder
JMP $value_ok ; reduction to 0..2*pi done
SN_COEFF DB 067h, 0B1h,0D4h ; -2.476053850842e-8
DB 06Eh,05Ch,08Bh,0B6h,0EBh,038h ; 2.755533965768e-6
DB 074h,0B5h,09Ch,0FCh,00Ch,0D0h ; -1.984126372865e-4
DB 07Ah,044h,086h,088h,088h,008h ; 8.333333325083e-3
DB 07Eh,0A9h,0AAh,0AAh,0AAh,0AAh ; -1.666666666663e-1
CS_COEFF DB 06Bh, 0C7h,091h ; -2.715314622037e-7
DB 071h,034h,0B7h,0A1h,006h,050h ; 2.479862035332e-5
DB 077h,02Eh,00Ah,058h,00Bh,0B6h ; -1.388887879411e-3
DB 07Ch,018h,0A0h,0AAh,0AAh,02Ah ; 4.166666651281e-2
DB 07Fh,0EFh,0FFh,0FFh,0FFh,0FFh ; -4.999999999923e-1
C1 DB 080h,000h,000h,000h,00Fh,0C9h ; pi/4-eps =
C2 DB 070h,0c2h,068h,021h,0a2h,0dah ; eps =
RSIN ENDP
ALIGN 4
CODE ENDS
END